#include <iostream>
#include <sstream>
#include <string>
#include <ros/ros.h>
#include <xf_control/xf_control.h>
#include "xf_control/xf_convert.hpp"

#include "../xf_demo/include/qisr.h"
#include "../xf_demo/include/msp_cmn.h"
#include "../xf_demo/include/msp_errors.h"
#include "../xf_demo/samples/asr_record_sample/speech_recognizer.h"

#include "rapidjson/document.h"

#define FRAME_LEN	640
#define	BUFFER_SIZE	4096
#define SAMPLE_RATE_16K     (16000)
#define SAMPLE_RATE_8K      (8000)
#define MAX_GRAMMARID_LEN   (32)
#define MAX_PARAMS_LEN      (1024)

const char * ASR_RES_PATH        = "fo|res/asr/common.jet"; //离线语法识别资源路径
const char * GRM_BUILD_PATH      = "res/asr/GrmBuilld"; //构建离线语法识别网络生成数据保存路径
const char * GRM_FILE            = "call.bnf"; //构建离线识别语法网络所用的语法文件

typedef struct _UserData {
    int     build_fini; //标识语法构建是否完成
    int     update_fini; //标识更新词典是否完成
    int     errcode; //记录语法构建或更新词典回调错误码
    char    grammar_id[MAX_GRAMMARID_LEN]; //保存语法构建返回的语法ID
}UserData;

enum {
    ACTION = 10000,
    COLOR = 1000,
    OBJTCT = 100
};

static char *g_result = NULL;
static unsigned int g_buffersize = BUFFER_SIZE;

void show_result(char *string, char is_over);
void on_result(const char *result, char is_last);
void on_speech_begin();
void on_speech_end(int reason);
int build_grammar(UserData *udata); //构建离线识别语法网络

int json_transform(char* json, WordsData& word_data); //json文件解析
int sr_get_status(struct speech_rec *sr);


int main(int argc, char *argv[])
{
    ros::init(argc, argv, "xf_control");
    ros::NodeHandle n;

    ros::Publisher control_pub = n.advertise<xf_control::xf_control>("xf_conttol/data", 1);

    const char *login_config    = "appid = 59423390"; //登录参数
    UserData asr_data;
    int ret                     = 0 ;

    ret = MSPLogin(NULL, NULL, login_config); //第一个参数为用户名，第二个参数为密码，传NULL即可，第三个参数是登录参数
    if (MSP_SUCCESS != ret) {
        printf("登录失败：%d\n", ret);
        return -1;
    }

    memset(&asr_data, 0, sizeof(UserData));
    printf("构建离线识别语法网络...\n");
    ret = build_grammar(&asr_data);  //第一次使用某语法进行识别，需要先构建语法网络，获取语法ID，之后使用此语法进行识别，无需再次构建
    if (MSP_SUCCESS != ret) {
        printf("构建语法调用失败！\n");
        return -1;
    }
    while (1 != asr_data.build_fini)
        usleep(300 * 1000);
    if (MSP_SUCCESS != asr_data.errcode)
        return -1;
    printf("离线识别语法网络构建完成，开始识别...\n");

    char asr_params[MAX_PARAMS_LEN]    = {NULL};

    //离线语法识别参数设置
    snprintf(asr_params, MAX_PARAMS_LEN - 1,
        "engine_type = local, \
        asr_res_path = %s, sample_rate = %d, \
        grm_build_path = %s, local_grammar = %s, \
        result_type = json, result_encoding = UTF-8",
        ASR_RES_PATH,
        SAMPLE_RATE_16K,
        GRM_BUILD_PATH,
        asr_data.grammar_id
    );

    struct speech_rec iat;
    struct speech_rec_notifier recnotifier = {
            on_result,
            on_speech_begin,
            on_speech_end
    };

    /* Basic initialization of curses lib
    initscr();
    cbreak();
    noecho();
    nonl();
    intrflush(stdscr, FALSE);
    keypad(stdscr, TRUE);
    // clear the screen
    wclear( stdscr );*/

    ros::Rate loop_rate(1000);
    while(ros::ok())
    {
        ros::Time t= ros::Time::now();

        int c = 0;
        c = getchar();
        //printf("get:%d",c);
        if(c == 3)
        {
            ROS_INFO("end");
            break;
        }
        else if(c == 32)
        {
            int errcode = sr_init(&iat, asr_params, SR_MIC, &recnotifier);
            if (errcode) {
                ROS_INFO("speech recognizer init failed\n");
                return -1;
            }
            errcode = sr_start_listening(&iat);
            if (errcode) {
                ROS_INFO("start listen failed %d\n", errcode);
            }
            // demo n seconds recording
            int i = 0;
            while (i++ < 6)
                sleep(1);

            errcode = sr_stop_listening(&iat);
            if (errcode) {
                ROS_INFO("stop listening failed %d\n", errcode);
            }
            sr_uninit(&iat);

            WordsData ws_data = {0, {0, 0, 0}, {0, 0, 0}};
            json_transform(g_result, ws_data);

            if (ws_data.score > 30) {
                xf_control::xf_control msg;
                xf_Convertor::toMessage(ws_data, ros::Time::now().toSec(), msg);
                control_pub.publish(msg);
            }
            printf("==========================\n");
        }

        ros::spinOnce();
    }
    //endwin();
    MSPLogout();

    return 0;
}

int build_grm_cb(int ecode, const char *info, void *udata)
{
    UserData *grm_data = (UserData *)udata;

    if (NULL != grm_data) {
        grm_data->build_fini = 1;
        grm_data->errcode = ecode;
    }

    if (MSP_SUCCESS == ecode && NULL != info) {
        printf("构建语法成功！ 语法ID:%s\n", info);
        if (NULL != grm_data)
            snprintf(grm_data->grammar_id, MAX_GRAMMARID_LEN - 1, info);
    }
    else
        printf("构建语法失败！%d\n", ecode);

    return 0;
}

int build_grammar(UserData *udata)
{
    FILE *grm_file                           = NULL;
    char *grm_content                        = NULL;
    unsigned int grm_cnt_len                 = 0;
    char grm_build_params[MAX_PARAMS_LEN]    = {NULL};
    int ret                                  = 0;

    grm_file = fopen(GRM_FILE, "rb");
    if(NULL == grm_file) {
        printf("打开\"%s\"文件失败！[%s]\n", GRM_FILE, strerror(errno));
        return -1;
    }

    fseek(grm_file, 0, SEEK_END);
    grm_cnt_len = ftell(grm_file);
    fseek(grm_file, 0, SEEK_SET);

    grm_content = (char *)malloc(grm_cnt_len + 1);
    if (NULL == grm_content)
    {
        printf("内存分配失败!\n");
        fclose(grm_file);
        grm_file = NULL;
        return -1;
    }
    fread((void*)grm_content, 1, grm_cnt_len, grm_file);
    grm_content[grm_cnt_len] = '\0';
    fclose(grm_file);
    grm_file = NULL;

    snprintf(grm_build_params, MAX_PARAMS_LEN - 1,
        "engine_type = local, \
        asr_res_path = %s, sample_rate = %d, \
        grm_build_path = %s, ",
        ASR_RES_PATH,
        SAMPLE_RATE_16K,
        GRM_BUILD_PATH
    );
    ret = QISRBuildGrammar("bnf", grm_content, grm_cnt_len, grm_build_params, build_grm_cb, udata);

    free(grm_content);
    grm_content = NULL;

    return ret;
}

void show_result(char *string, char is_over)
{
    printf("\rResult: [ %s ]", string);
    if(is_over)
        putchar('\n');
}

void on_result(const char *result, char is_last)
{
    if (result) {
        size_t left = g_buffersize - 1 - strlen(g_result);
        size_t size = strlen(result);
        if (left < size) {
            g_result = (char*)realloc(g_result, g_buffersize + BUFFER_SIZE);
            if (g_result)
                g_buffersize += BUFFER_SIZE;
            else {
                ROS_INFO("mem alloc failed\n");
                return;
            }
        }
        strncat(g_result, result, size);
        //show_result(g_result, is_last);
    }
}
void on_speech_begin()
{
    if (g_result)
    {
        free(g_result);
    }
    g_result = (char*)malloc(BUFFER_SIZE);
    g_buffersize = BUFFER_SIZE;
    memset(g_result, 0, g_buffersize);

    ROS_INFO("Start Listening...");
}
void on_speech_end(int reason)
{
    if (reason == END_REASON_VAD_DETECT)
        ROS_INFO("Speaking done ");
    else
        ROS_INFO("Recognizer error %d", reason);
}

int json_transform(char* json, WordsData& word_data)
{
    //printf("Result: [ %s ]\n", json);
    std::string s_word;
    std::stringstream ss;

    rapidjson::Document doc;
    doc.Parse(json);

    word_data.score = 0;

    if(!doc.IsObject())
        return 0;

    if(doc.HasMember("sc"))
    {
        assert(doc["sc"].IsInt());
        word_data.score = doc["sc"].GetInt();
    }

    if(doc.HasMember("ws"))
    {
        assert(doc["ws"].IsArray());

        const rapidjson::Value &a = doc["ws"];
        for (rapidjson::Value::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
        {
            if (itr->IsObject())
            {
                const rapidjson::Value &iword = *itr;
                if (iword.HasMember("slot") && iword.HasMember("cw"))
                {
                    assert(iword["cw"].IsArray());
                    int size = iword["cw"].Size();
                    const rapidjson::Value &word = iword["cw"][0];

                    assert(word.HasMember("id") && word.HasMember("sc"));
                    assert(word["id"].IsInt());
                    assert(word["sc"].IsInt());
                    assert(word.HasMember("w"));

                    int id = word["id"].GetInt();
                    int score = word["sc"].GetInt();
                    int n = id & (ACTION | COLOR | OBJTCT);
                    int type = id % 10;

                    ss << word["w"].GetString();
                    ss << "(";
                    ss << score ;
                    ss <<  ")";

                    switch (n)
                    {
                        case ACTION:
                            word_data.words[0] = type;
                            word_data.scores[0] = score;
                            break;
                        case COLOR :
                            word_data.words[1] = type;
                            word_data.scores[1] = score;
                            break;
                        case OBJTCT:
                            word_data.words[2] = type;
                            word_data.scores[2] = score;
                            break;
                    }
                }
            }
        }
    }

    ss << " sc:" << word_data.score;
    s_word = ss.str();
    printf("Result: [ %s ]\n", s_word.c_str());
    return 0;
}
